package com.kpmg.datalake.autopackage.controller;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.kpmg.datalake.autopackage.model.IndustryTemplateManager;
import com.kpmg.datalake.autopackage.service.ExcelTemplateService;
import com.kpmg.datalake.autopackage.service.IndustryTemplateService;
import com.kpmg.datalake.autopackage.utils.ExcelOperateUtil;
import com.kpmg.datalake.autopackage.utils.MyThreadPoolExecutor;
import com.kpmg.datalake.autopackage.vo.ExcelTemplate;
import com.kpmg.datalake.autopackage.vo.TargetTemplateVO;
import com.kpmg.datalake.common.enums.ResultCodeEnum;
import com.kpmg.datalake.common.utils.ContextHolderUtils;
import com.kpmg.datalake.common.utils.ResponseUtils;
import com.kpmg.datalake.common.utils.Utils;
import com.kpmg.datalake.common.vo.ServerResponse;
import com.kpmg.datalake.db.model.Project;
import com.kpmg.datalake.db.model.SysUsr;
import com.kpmg.datalake.operatelog.annotation.OptLog;
import com.kpmg.datalake.operatelog.enums.LogTypeEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

/**
 * excel页面展示控制器
 * 
 * @author james huang
 */
@RestController
@Api("PkgAutoController Package自动化操作控制器")
public class PkgExcelAutoController {
  private static final Logger logger = LoggerFactory.getLogger(PkgExcelAutoController.class);

  @Autowired
  private ExcelTemplateService excelTemplateService;

  @Autowired
  private IndustryTemplateService industryTemplateService;

  @Value("${template.path}")
  private String templatePath;
  /**
   * 查询锁
   */
  private ReentrantLock queryLock = new ReentrantLock();

  /**
   * 线程池核心线程数
   */
  private static final int CORE_POOL_SIZE = 5;

  /**
   * 线程池最大线程数
   */
  private static final int MAX_POOL_SIZE = 10;

  /**
   * 额外线程空状态生存时间
   */
  private static final int KEEP_ALIVE_TIME = 30 * 1000;

  /**
   * 阻塞队列。当核心线程都被占用,且阻塞队列已满的情况下,才会开启额外线程。
   */
  private static BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(CORE_POOL_SIZE);

  private static ThreadFactory factory = new ThreadFactory() {
    private final AtomicInteger integer = new AtomicInteger();

    @Override
    public Thread newThread(Runnable r) {
      return new Thread(r, "ThreadPool thread: " + integer.getAndIncrement());
    }
  };

  private static ThreadPoolExecutor threadPool = new MyThreadPoolExecutor(CORE_POOL_SIZE,
      MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, workQueue, factory);

  private static final String DESCRIPTION = "Description";
  private static final String SHEET_NAME = "sheetName";
  private static final String UPLOAD_UNFINISHED = "1";
  private static final String UPLOAD_FINISHED = "2";
  private static final String UPLOAD_ERROR = "3";



  /**
   * 跳转到查询数据报表
   *
   * @return
   */
  @ApiOperation(value = "跳转到查询数据报表", notes = "跳转到查询数据报表")
  @GetMapping(value = "/searchPkgData")
  public ModelAndView searchPkgData(HttpSession session) {
    ModelAndView view = new ModelAndView();
    view.setViewName("searchPkg_data");

    return view;
  }

  /**
   * 下载文件
   * 
   * @param targetTemplateVO
   * @param request
   * @param response
   */
  @ApiOperation(value = "下载文件", notes = "下载文件")
  @PostMapping(value = "/targetManage/resultPkgDownload")
  @OptLog(logType = LogTypeEnum.OPERATION, module = "autoPackage", description = "下载目标模板")
  public void requestResultDownload (
      @RequestBody TargetTemplateVO targetTemplateVO, HttpServletRequest request,
      HttpServletResponse response) throws IOException {
    String tplNm = targetTemplateVO.getTplNm();
    String filePath = targetTemplateVO.getFileUrl();
    ResponseUtils.setZipDownloadResponseHeader(tplNm + ".xlsx", request, response);

    try (FileInputStream inputStream = new FileInputStream(filePath)) {
      IOUtils.copy(inputStream, response.getOutputStream());
      response.flushBuffer();
    } catch (Exception e) {
      logger.error("下载查询请求结果文件异常:" + e.getMessage(), e);
      try (OutputStream outputStream = response.getOutputStream()) {
        String outString = JSONObject.toJSONString(ServerResponse.createByErrorCodeMessage(
            ResultCodeEnum.CODE4041.getCode(), e.getLocalizedMessage()));
        outputStream.write(outString.getBytes());
      }
    }

  }

  /**
   * 查询请求列表
   * 
   * @param targetTemplateVO
   * @return
   */
  @PostMapping(value = "/targetManage/showPkgRqsList")
  @OptLog(logType = LogTypeEnum.OPERATION, module = "autoPackage", description = "查询目标模板")
  public ServerResponse<PageInfo<ExcelTemplate>> showRqsList(
      @RequestBody TargetTemplateVO targetTemplateVO) {
    try {
      // 获取请求记录列表
      return excelTemplateService.qryTargetTemplate(targetTemplateVO);

    } catch (Exception e) {
      logger.error("请求列表展示异常:", e.getMessage(), e);
      return ServerResponse.createByErrorMessage(e.getMessage());
    }

  }

  /**
   * 查询上传任务数
   * 
   * @return
   */
  @PostMapping(value = "/targetManage/queryTaskCount")
  public ServerResponse<Integer> queryTaskCount() {
    queryLock.lock();
    try {
      Integer count = excelTemplateService.queryTaskCount();

      return ServerResponse.createBySuccess(count);
    } catch (Exception e) {
      logger.error("查询上传任务数异常:" + e.getMessage(), e);
    } finally {
      queryLock.unlock();
    }
    return ServerResponse.createBySuccess();
  }

  /**
   * 新增目标模板
   * 
   * @param file
   * @param targetTemplateVO
   * @return
   */
  @PostMapping(value = "/targetManage/addExcelTemplate")
  @OptLog(logType = LogTypeEnum.OPERATION, module = "autoPackage", description = "添加目标模板")
  public ServerResponse<String> addExcelTemplate(MultipartFile file,
      TargetTemplateVO targetTemplateVO, HttpSession session) {
    ExcelTemplate newExc = new ExcelTemplate();
    try {
      // 获取用户项目信息
      SysUsr user = (SysUsr) session.getAttribute("user");

      Project project = Utils.getProject(targetTemplateVO.getProjectId(), user);

      String uploadPath = System.getProperty(templatePath);
      String absolutePath = ExcelOperateUtil.handleFileStream(file, uploadPath, user);

      newExc.setUploadId(user.getUsrId());
      newExc.setClientId(targetTemplateVO.getProjectId());
      newExc.setTplNm(targetTemplateVO.getFileName());
      newExc.setFileUrl(absolutePath);
      if (StringUtils.isEmpty(targetTemplateVO.getRemark())) {
        newExc.setRemark("");
      } else {
        newExc.setRemark(targetTemplateVO.getRemark().trim());
      }
      // 从目标模板管理过来，新增的都是手动添加
      BigDecimal tplType = new BigDecimal("0");
      newExc.setTplType(tplType);
      newExc.setClientNm(targetTemplateVO.getProjectName());
      newExc.setDelInd(new BigDecimal(0));
      newExc.setUploadStatus(UPLOAD_FINISHED);

      excelTemplateService.insertClientManage(user, newExc, "", project);
    } catch (Exception e) {
      File dir = new File(newExc.getFileUrl());
      FileUtils.deleteQuietly(dir);
      logger.error("数据导出请求异常:" + e.getMessage(), e);
      return ServerResponse.createByErrorMessage("目标模板新增失败");
    }
    return ServerResponse.createBySuccess();
  }

  /**
   * 修改目标模板
   * 
   * @return
   */
  @PostMapping(value = "/targetManage/editExcelTemplate")
  @OptLog(logType = LogTypeEnum.OPERATION, module = "autoPackage", description = "修改目标模板")
  public ServerResponse<String> editExcelTemplate(MultipartFile file,
      TargetTemplateVO targetTemplateVO, HttpSession session) {
    ExcelTemplate newExc = new ExcelTemplate();
    try {
      // 获取用户项目信息
      SysUsr user = (SysUsr) session.getAttribute("user");

      if (StringUtils.isEmpty(targetTemplateVO.getRemark())) {
        newExc.setRemark("");
      } else {
        newExc.setRemark(targetTemplateVO.getRemark());
      }

      if (file != null) {
        String uploadPath = System.getProperty(templatePath);
        String absolutePath = ExcelOperateUtil.handleFileStream(file, uploadPath, user);

        newExc.setFileUrl(absolutePath);
      }

      newExc.setTplNm(targetTemplateVO.getTplNm());
      newExc.setUploadStatus(UPLOAD_FINISHED);
      newExc.setTplMgtId(targetTemplateVO.getTplMgtId());
      excelTemplateService.editClientManage(user, newExc, "", new Project());
    } catch (Exception e) {
      logger.error("数据导出请求异常:" + e.getMessage(), e);
      File dir = new File(newExc.getFileUrl());
      FileUtils.deleteQuietly(dir);
      return ServerResponse.createByErrorMessage("目标模板更新失败");
    }
    return ServerResponse.createBySuccess();
  }

  /**
   * 配置目标模板
   *
   * @return
   */
  @PostMapping(value = "/targetManage/configureTargetTemplate")
  @OptLog(logType = LogTypeEnum.OPERATION, module = "autoPackage", description = "配置目标模板")
  public ServerResponse<String> configureIndustryTemplate(@RequestBody TargetTemplateVO excelVo) {
    ExcelTemplate newExc = new ExcelTemplate();
    SysUsr user = ContextHolderUtils.getLoginUser();
    try {
      newExc = handleEntity(excelVo, user.getUsrId());
      newExc.setUploadStatus(UPLOAD_UNFINISHED);

      String userId = user.getUsrId();
      String tplId = newExc.getTplMgtId();

      excelTemplateService.editClientManage(user, newExc, "", new Project());

      threadPool.execute(() -> configureIndustryTemplateThread(excelVo, tplId, userId));

      return ServerResponse.createBySuccess();
    } catch (Exception e) {
      newExc.setUploadStatus(UPLOAD_ERROR);
      try {
        excelTemplateService.editClientManage(user, newExc, "", new Project());
      } catch (Exception e1) {

        logger.error(ResultCodeEnum.CODE5000.getDisplay() + e1.getMessage(), e1);
      }
      return ServerResponse.createByErrorMessage("目标模板配置失败");
    }
  }

  /**
   * 配置目标模板
   *
   * @return
   */
  private void configureIndustryTemplateThread(TargetTemplateVO excelVo, String tplId,
      String userId) {
    ExcelTemplate newExc = new ExcelTemplate();
    SysUsr user = new SysUsr();
    user.setUsrId(userId);
    try {
      ExcelTemplate excelTemplate = excelTemplateService.selectByPrimaryKey(excelVo.getTplMgtId());
      IndustryTemplateManager industryTemplateManager =
          industryTemplateService.selectByPrimaryKey(excelTemplate.getIndustryId());

      String filePath = excelTemplate.getFileUrl();
      JSONArray jsonArray = JSON.parseArray(excelVo.getSelect());
      Workbook workbook = ExcelOperateUtil.getWorkbook(industryTemplateManager.getFileUrl());

      String[] totalSheetArr = ExcelOperateUtil.getSheets(workbook);
      int size = jsonArray.size();
      int totalSize = totalSheetArr.length;

      String[] select;
      String[] unSelect;
      String[] newArr;
      if (ArrayUtils.contains(totalSheetArr, DESCRIPTION)) {
        unSelect = new String[(totalSize - size - 1)];
        select = new String[size + 1];
        select[size] = DESCRIPTION;
        newArr = new String[totalSize - 1];
      } else {
        newArr = new String[totalSize];
        select = new String[size];
        unSelect = new String[(totalSize - size)];
      }
      int count = 0;
      for (int i = 0; i < size; i++) {
        JSONObject jsonObject = (JSONObject) jsonArray.get(i);
        String sheetName = jsonObject.getString(SHEET_NAME);
        select[i] = sheetName;
        newArr[i] = sheetName;
        count++;
      }
      totalSheetArr = ArrayUtils.removeElement(totalSheetArr, DESCRIPTION);
      int j = 0;
      for (String aTotalSheetArr : totalSheetArr) {
        if (!ArrayUtils.contains(select, aTotalSheetArr)) {
          unSelect[j++] = aTotalSheetArr;
        }
        if (!ArrayUtils.contains(newArr, aTotalSheetArr)) {
          newArr[count++] = aTotalSheetArr;
        }
      }

      ExcelOperateUtil.createCustomTemplate(workbook, filePath, select, unSelect);

      List<Map<String, String>> sheetNameList = ExcelOperateUtil.getSheetNameList(workbook, newArr);
      StringBuilder newOrderSheet = new StringBuilder();
      if (!ArrayUtils.isEmpty(newArr)) {
        Arrays.stream(newArr).forEach(arr -> newOrderSheet.append(arr).append(","));
      }

      List<Map<String, String>> sheetList = ExcelOperateUtil.convertJsonArrayToList(jsonArray);
      newExc.setSelectSheet(JSON.toJSONString(sheetList));

      if (!StringUtils.isEmpty(newOrderSheet)) {
        newExc.setTotalSheet(newOrderSheet.toString().substring(0, newOrderSheet.length() - 1));
      }
      if (!CollectionUtils.isEmpty(sheetNameList)) {
        newExc.setSheetDesc(JSON.toJSONString(sheetNameList));
      }
      newExc.setUploadStatus(UPLOAD_FINISHED);
      newExc.setTplMgtId(tplId);

      excelTemplateService.editClientManage(user, newExc, "", new Project());
    } catch (Exception e) {
      logger.error(ResultCodeEnum.CODE5000.getDisplay() + e.getMessage(), e);
      newExc.setUploadStatus(UPLOAD_ERROR);
      try {
        excelTemplateService.editClientManage(user, newExc, "", new Project());
      } catch (Exception e1) {
        logger.error(ResultCodeEnum.CODE5000.getDisplay() + e1.getMessage(), e1);
      }
    }
  }

  /**
   * 删除目标模板
   * 
   * @return
   */
  @PostMapping(value = "/targetManage/delExcelTemplate")
  @OptLog(logType = LogTypeEnum.OPERATION, module = "autoPackage", description = "删除目标模板")
  public ServerResponse<String> delTargetTemplate(@RequestBody TargetTemplateVO targetTemplateVO,
      HttpSession session) {
    try {
      SysUsr user = (SysUsr) session.getAttribute("user");
      ExcelTemplate delExc = new ExcelTemplate();
      delExc.setDelInd(new BigDecimal(1));
      delExc.setTplId(user.getUsrId());
      delExc.setTplMgtId(targetTemplateVO.getTplMgtId());
      excelTemplateService.editClientManage(user, delExc, "", new Project());
    } catch (Exception e) {
      logger.error("删除目标模板异常：" + e.getMessage(), e);
      return ServerResponse.createByErrorMessage("删除目标模板失败");
    }
    return ServerResponse.createBySuccess();
  }


  /**
   * 处理目标模板实体类
   * 
   * @param excelVo
   * @param uploadId
   * @return
   */
  private ExcelTemplate handleEntity(TargetTemplateVO excelVo, String uploadId) {
    ExcelTemplate newExc = new ExcelTemplate();

    newExc.setUploadId(uploadId);
    newExc.setTplNm(excelVo.getFileName());
    newExc.setTplType(new BigDecimal(1));
    if (StringUtils.isEmpty(excelVo.getRemark())) {
      newExc.setRemark("");
    } else {
      newExc.setRemark(excelVo.getRemark().trim());
    }
    newExc.setDelInd(new BigDecimal(0));
    newExc.setTplMgtId(excelVo.getTplMgtId());

    return newExc;
  }
}
